﻿package org.deft.tween {

	import flash.display.Shape;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;

	import org.deft.timer.DWatchItem;

	/**
	 * 控制一个对象的 一个或多个属性缓动动画
	 *
	 */
	public final class DTween extends EventDispatcher {
		private var propList:Vector.<DTweenProp>;


		private var _target:Object = null;

		/**
		 * 要做动画的对象
		 */
		public function get target():Object {
			return _target;
		}

		private var watchItem:DWatchItem = null;

		private var _finished:Boolean = false;

		public function get finished():Boolean {
			return _finished;
		}

		/**
		 * 动画的持续时间，（最长的属性的动画的时间和重复播放次数决定）
		 * @return
		 *
		 */
		public function get duration():Number {
			var p:Number = 0;
			for each (var o:DTweenProp in propList) {
				if (repeat == -1) {
					p = Number.MAX_VALUE;
				} else if (o.duration > p) {
					p = o.duration;
				}
			}

			if (repeat > 0) {
				p *= repeat;
			}
			return p;
		}

		/**
		 * 重复次数，默认为1
		 * -1表示无限循环，永远不会发送完成事件
		 */
		public var repeat:int;

		//已经播放次数
		private var currentTimes:int = 0;

		/**
		 * 如果repeat > 0, yoyo = true,则播放完一次后会从结束点回退到开始点而不是直接从开始点播放
		 */
		public var yoyo:Boolean = false;

		/**
		 * 控制target对象的一个或多个属性缓动动画
		 * @param target 要做动画的对象
		 * @param repeat 播放的次数，-1表示无限循环，永远不会发送完成事件
		 * @param yoyo 如果repeat > 0, yoyo = true,则播放完一次后会从结束点回退到开始点而不是直接从开始点播放
		 *
		 */
		public function DTween(target:Object, repeat:int = 1, yoyo:Boolean = false) {
			this._target = target;
			this.repeat = repeat;
			this.yoyo = yoyo;

			this.propList = new Vector.<DTweenProp>();

			if (_shape == null) {
				_shape = new Shape();
				_shape.addEventListener(Event.ENTER_FRAME, updateAll);
			}

			watchItem = new DWatchItem(-1, 0);

			addTween(this);
		}

		//
		private static function addTween(tween:DTween):void {
			tweenDic[tween] = tween;
		}

		/**
		 * 销毁指定的tween
		 * @param tween
		 *
		 */
		public static function removeTween(tween:DTween):void {
			tween.disable();
			delete tweenDic[tween];
		}

		/**
		 * 销毁所有的tween
		 *
		 */
		public static function removeAllTween():void {
			for each (var tween:DTween in tweenDic) {
				tween.disable();
				delete tweenDic[tween];
			}
		}
		//
		private static var tweenDic:Dictionary = new Dictionary();
		private static var _shape:Shape = null;

		//
		private static function updateAll(e:* = null):void {
			for each (var tween:DTween in tweenDic) {
				tween.update();
			}
		}

		/**
		 *
		 * @param target 目标对象
		 * @param duration 持续时间，单位：毫秒
		 * @param prop target要做缓动动画的属性名字
		 * @param begin 指定缓动属性和开始值
		 * @param finish 指定缓动的属性和结束值
		 * @param ease 缓动函数
		 * @param completeCallback 动画完成后的回调函数
		 */
		public static function fromTo(target:Object, duration:Number, prop:String, begin:Number, finish:Number,
									  ease:Function = null, completeCallback:Function = null):void {
			var tween:DTween = new DTween(target);
			tween.modifyProp(prop, begin, finish, duration, ease);
			tween.start();
			tween.addEventListener(DTweenEvent.MOTION_FINISH, function(e:DTweenEvent):void {
				if (completeCallback != null) {
					completeCallback();
				}
				tween.disable();
			});
		}

		/**
		 * 从当前位置变化到finished指定的位置
		 * @param target 目标对象
		 * @param duration 持续时间，单位：毫秒
		 * @param prop target要做缓动动画的属性名字
		 * @param finish 指定缓动的属性和结束值
		 * @param ease 缓动函数
		 * @param completeCallback 动画完成后的回调函数
		 */
		public static function to(target:Object, duration:Number, prop:String, finish:Number, ease:Function = null,
								  completeCallback:Function = null):void {
			fromTo(target, duration, prop, target[prop], finish, ease, completeCallback);
		}

		/**
		 * 从begin指定位置变化到当前位置
		 * @param target 目标对象
		 * @param duration 持续时间，单位：毫秒
		 * @param prop target要做缓动动画的属性名字
		 * @param begin 指定缓动属性和开始值
		 * @param ease 缓动函数
		 * @param completeCallback 动画完成后的回调函数
		 */
		public static function from(target:Object, duration:Number, prop:String, begin:Number, ease:Function = null,
									completeCallback:Function = null):void {
			fromTo(target, duration, prop, begin, target[prop], ease, completeCallback);
		}

		/**
		 * 开始播放
		 */
		public function start():void {
			_finished = false;
			watchItem.reset();
			watchItem.start();
			dispatchEvent(new DTweenEvent(DTweenEvent.MOTION_START));
		}

		/**
		 * 停止播放
		 */
		public function stop():void {
			watchItem.stop();
			this.dispatchEvent(new DTweenEvent(DTweenEvent.MOTION_STOP));
		}

		/**
		 * 暂停
		 */
		public function pause():void {
			watchItem.pause();
			this.dispatchEvent(new DTweenEvent(DTweenEvent.MOTION_PAUSE));
		}

		/**
		 * 继续播放
		 */
		public function resume():void {
			watchItem.start();
			this.dispatchEvent(new DTweenEvent(DTweenEvent.MOTION_RESUME));
		}

		/**
		 * 更新属性
		 */
		private function update():void {
			if (watchItem.getIsPaused() || watchItem.getIsStoped())
				return;
			var usedmillisecond:Number = watchItem.update();
			var finishedAll:Boolean = true;
			for each (var obj:DTweenProp in propList) {
				obj.update(usedmillisecond);
				target[obj.prop] = obj.position;

				if (obj.completeRate < 1) {
					finishedAll = false;
				}
			}

			dispatchEvent(new DTweenEvent(DTweenEvent.MOTION_CHANGE));

			if (finishedAll) {
				currentTimes += 1;
				watchItem.stop();
				if (repeat == -1 || currentTimes < repeat) {
					if (yoyo) {
						backPlay();
					} else {
						start();
					}
				} else {
					currentTimes = 0;
					_finished = true;
					if (yoyo && repeat % 2 == 0) {
						back();
					}
					dispatchEvent(new DTweenEvent(DTweenEvent.MOTION_FINISH));
				}
			}
		}

		private function back():void {
			for each (var obj:DTweenProp in propList) {
				obj.back();
			}
		}

		/**
		 * 反向播放
		 */
		public function backPlay():void {
			back();
			start();
		}

		/**
		 * 修改或添加一个要做动画的对象的属性相关参数
		 * @param prop 属性名称
		 * @param begin 开始值
		 * @param finish 结束值
		 * @param duration 持续毫秒数
		 * @param easeFun 缓动函数
		 * @return
		 *
		 */
		public function modifyProp(prop:String, begin:Number, finish:Number, duration:Number,
								   easeFun:Function = null):void {

			var obj:DTweenProp = null;
			for each (var o:DTweenProp in propList) {
				if (o.prop == prop) {
					obj = o;
					break;
				}
			}

			if (obj == null) {
				obj = new DTweenProp(prop, begin, finish, duration, easeFun);
				propList.push(obj);
			} else {
				obj.begin = begin;
				obj.finish = finish;
				obj.duration = duration;
				obj.easeFun = easeFun;
			}

		}

		/**
		 * 移除目标对象指定名称属性的缓动动画
		 * @param	prop 属性名称
		 * @return
		 */
		public function removeProp(prop:String):void {

			for (var i:int = 0; i < propList.length; i++) {
				if (propList[i].prop == prop) {
					propList.splice(i, 1);
					break;
				}
			}

		}

		/**
		 * 移除目标对象所有做缓动的属性
		 */
		public function removeAllProp():void {
			propList.splice(0, propList.length);
		}

		/**
		 * 使当前的Tween失去作用
		 * 不能被回复使用了，一但调用此方法，意味着这个对象应该被回收了
		 */
		public function disable():void {
			removeAllProp();
			_target = null;
			delete tweenDic[this];
		}

	}
}
